package com.katesoft.scale4j.rttp.jobs.jdbc.init;

import com.katesoft.scale4j.log.Logger;
import com.katesoft.scale4j.log.LogFactory;
import com.katesoft.scale4j.persistent.spring.LocalAnnotationSessionFactory;
import com.katesoft.scale4j.persistent.utils.DataAccessUtility;
import com.katesoft.scale4j.rttp.hibernate.SpringHazelcastBridge;
import com.katesoft.scale4j.rttp.internal.IRttpHazelcastBridgeAware;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.FirebirdDialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.dialect.InformixDialect;
import org.hibernate.dialect.MySQL5InnoDBDialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.MySQLInnoDBDialect;
import org.hibernate.dialect.Oracle8iDialect;
import org.hibernate.dialect.PointbaseDialect;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.dialect.SAPDBDialect;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.dialect.SybaseASE15Dialect;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Collections;

/** @author kate2007 */
abstract class AbstractSchemaCreator extends ResourceDatabasePopulator implements IRttpHazelcastBridgeAware, InitializingBean
{
    protected Logger logger = LogFactory.getLogger(getClass());
    protected Class<? extends Dialect> dialect;
    protected String tablesPrefix;
    protected SpringHazelcastBridge springHazelcastBridge;
    //
    private ClassPathResource schedulerCreationScript;
    private ClassPathResource batchCreationScript;
    private LocalAnnotationSessionFactory sessionFactory;

    /**
     * check if all tables from given collection already created/exist in database.
     *
     * @param connection actual jdbc connection
     * @return true if all tables from given collection exists.
     * @throws SQLException if JDBC exception occurred
     */
    protected boolean tablesCreated(Connection connection) throws SQLException
    {
        Collection<String> tables = tables();
        return DataAccessUtility.allTablesExist(connection, tables.toArray(new String[tables.size()]));
    }

    @Override
    public void afterPropertiesSet() throws Exception
    {
        if (dialect == null) {
            dialect = Dialect.getDialect(sessionFactory.getConfiguration().getProperties()).getClass();
        }
        setIgnoreFailedDrops(true);
        String quartzSchemaResource = null;
        String batchSchemaResource = null;
        if (Oracle8iDialect.class.isAssignableFrom(dialect)) {
            quartzSchemaResource = TABLES_QUARTZ_ORACLE;
            batchSchemaResource = TABLES_BATCH_ORACLE;
        }
        else if (PostgreSQLDialect.class.isAssignableFrom(dialect)) {
            quartzSchemaResource = TABLES_QUARTZ_POSTGRES;
            batchSchemaResource = TABLES_BATCH_POSTGRES;
        }
        else if (MySQLInnoDBDialect.class.isAssignableFrom(dialect) || MySQL5InnoDBDialect.class.isAssignableFrom(dialect)) {
            quartzSchemaResource = TABLES_QUARTZ_MYSQL_INNODB;
            batchSchemaResource = TABLES_BATCH_MYSQL;
        }
        else if (MySQLDialect.class.isAssignableFrom(dialect)) {
            quartzSchemaResource = TABLES_QUARTZ_MYSQL;
            batchSchemaResource = TABLES_BATCH_MYSQL;
        }
        else if (DerbyDialect.class.isAssignableFrom(dialect)) {
            quartzSchemaResource = TABLES_QUARTZ_DERBY;
            batchSchemaResource = TABLES_BATCH_DERBY;
        }
        else if (DB2Dialect.class.isAssignableFrom(dialect)) {
            quartzSchemaResource = TABLES_QUARTZ_DB2;
            batchSchemaResource = TABLES_BATCH_DB2;
        }
        else if (FirebirdDialect.class.isAssignableFrom(dialect)) { quartzSchemaResource = TABLES_QUARTZ_FIREBIRD;}
        else if (HSQLDialect.class.isAssignableFrom(dialect)) {
            quartzSchemaResource = TABLES_QUARTZ_HSQLDB;
            batchSchemaResource = TABLES_BATCH_HSQLDB;
        }
        else if (InformixDialect.class.isAssignableFrom(dialect)) { quartzSchemaResource = TABLES_QUARTZ_INFORMIX;}
        else if (PointbaseDialect.class.isAssignableFrom(dialect)) { quartzSchemaResource = TABLES_QUARTZ_POINTBASE;}
        else if (SAPDBDialect.class.isAssignableFrom(dialect)) { quartzSchemaResource = TABLES_QUARTZ_SAPDB;}
        else if (SQLServerDialect.class.isAssignableFrom(dialect)) {
            quartzSchemaResource = TABLES_QUARTZ_SQL_SERVER;
            batchSchemaResource = TABLES_BATCH_SQL_SERVER;
        }
        else if (SybaseASE15Dialect.class.isAssignableFrom(dialect)) {
            quartzSchemaResource = TABLES_QUARTZ_SYBASE;
            batchSchemaResource = TABLES_BATCH_SYBASE;
        }
        else if (H2Dialect.class.isAssignableFrom(dialect)) {
            quartzSchemaResource = TABLES_QUARTZ_H2;
            batchSchemaResource = TABLES_BATCH_H2;
        }
        if (quartzSchemaResource != null) {
            schedulerCreationScript = new ClassPathResource(String.format("META-INF/quartz/%s", quartzSchemaResource));
            logger.info("found %s for quartz schema creation", schedulerCreationScript.getURL().toExternalForm());
        }
        if (batchSchemaResource != null) {
            batchCreationScript = new ClassPathResource(String.format("org/springframework/batch/core/%s", batchSchemaResource));
            logger.info("found %s for batch schema creation", batchCreationScript.getURL().toExternalForm());
        }
    }

    public static final String TABLES_QUARTZ_ORACLE = "tables_oracle.sql";
    public static final String TABLES_QUARTZ_POSTGRES = "tables_postgres.sql";
    public static final String TABLES_QUARTZ_MYSQL_INNODB = "tables_mysql_innodb.sql";
    public static final String TABLES_QUARTZ_MYSQL = "tables_mysql.sql";
    public static final String TABLES_QUARTZ_DERBY = "tables_derby.sql";
    public static final String TABLES_QUARTZ_DB2 = "tables_db2.sql";
    public static final String TABLES_QUARTZ_FIREBIRD = "tables_firebird.sql";
    public static final String TABLES_QUARTZ_HSQLDB = "tables_hsqldb.sql";
    public static final String TABLES_QUARTZ_H2 = "tables_h2.sql";
    public static final String TABLES_QUARTZ_INFORMIX = "tables_informix.sql";
    public static final String TABLES_QUARTZ_POINTBASE = "tables_pointbase.sql";
    public static final String TABLES_QUARTZ_SAPDB = "tables_sapdb.sql";
    public static final String TABLES_QUARTZ_SQL_SERVER = "tables_sqlServer.sql";
    public static final String TABLES_QUARTZ_SYBASE = "tables_sybase.sql";
    //
    public static final String TABLES_BATCH_ORACLE = "schema-oracle10g.sql";
    public static final String TABLES_BATCH_POSTGRES = "schema-postgresql.sql";
    public static final String TABLES_BATCH_SYBASE = "schema-sybase.sql";
    public static final String TABLES_BATCH_SQL_SERVER = "schema-sqlserver.sql";
    public static final String TABLES_BATCH_MYSQL = "schema-mysql.sql";
    public static final String TABLES_BATCH_HSQLDB = "schema-hsqldb.sql";
    public static final String TABLES_BATCH_H2 = "schema-h2.sql";
    public static final String TABLES_BATCH_DB2 = "schema-db2.sql";
    public static final String TABLES_BATCH_DERBY = "schema-derby.sql";

    /**
     * allows to set custom hibernate dialect.
     * <p/>
     * dialect is not required property and will be detected automatically if not supplied.
     *
     * @param dialect hibernate dialect.
     */
    public void setDialect(Class<? extends Dialect> dialect)
    {
        this.dialect = dialect;
    }

    @Required
    public void setSessionFactory(LocalAnnotationSessionFactory s)
    {
        this.sessionFactory = s;
    }

    @Override
    @Required
    public void setRttpHazelcastBridge(SpringHazelcastBridge springHazelcastBridge)
    {
        this.springHazelcastBridge = springHazelcastBridge;
    }

    public void setTablesPrefix(String tablesPrefix)
    {
        this.tablesPrefix = tablesPrefix;
    }

    public ClassPathResource getSchedulerCreationScript()
    {
        return schedulerCreationScript;
    }

    public ClassPathResource getBatchCreationScript()
    {
        return batchCreationScript;
    }

    /** @return collection of tables that should exist after schema creation. */
    protected Collection<String> tables()
    {
        return Collections.emptySet();
    }

    public Class<? extends Dialect> getDialect()
    {
        return dialect;
    }
}
